/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tigris.jasmincc.framework.implementation;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;

import org.tigris.jasmincc.Production;
import org.tigris.jasmincc.framework.*;
import org.tigris.mappedlexer.*;
import org.tigris.mappedlexer.io.*;
import org.tigris.mappedlexer.io.implementation.FileCharInputSequence;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A default implementation of cross compiler interface.
 * 
 * @author Sam Huang
 * 
 */
public class SimpleCoordinator extends AbstractCoordinator {

	protected MappedLexer mappedLexer = null;

	protected CharInputSequence charInputSequence = null;

	/**
	 * Get the first part of grammar name to come up with the namespace name.
	 * 
	 * @param grammarName
	 * @return namespace name
	 */
	public static String getNamespaceByGrammarName(String grammarName) {
		int dot = grammarName.lastIndexOf('.');
		String namespaceName = grammarName.substring(0, dot);
		return namespaceName;
	}

	// protected Messenger messenger = null;
	protected Log logger = LogFactory.getLog(SimpleCoordinator.class);

	protected Production grammarProduction = null;

	protected boolean isPrimaryGrammar = true;

	protected List<NamingManager> processed = null;

	protected Queue<NamingManager> queue = null;

	protected DefaultParserHandler defaultParserHandler = null;

	/**
	 * Defualt constructor
	 * 
	 */
	public SimpleCoordinator() {
		queue = new ConcurrentLinkedQueue<NamingManager>();
		processed = new ArrayList<NamingManager>();
	}

	/**
	 * Add a new grammar to be processed.
	 * 
	 * @param instanceName
	 *            the instance name of the grammar
	 * @param grammarname
	 *            the grammar name
	 * @param instances
	 *            instances with their qualified names
	 */
	public void addGrammar(String instanceName, String grammarName,
			Map<String, String> instances) throws IllegalArgumentException {
		if (instances == null) {
			throw new IllegalArgumentException("instances cannot be null");
		}
		// messenger.println("grammar: " + grammarName + " - instance: " +
		// instanceName);
		isAlreadyProcessed(instanceName, grammarName);

		NamingManager namingManager = new SimpleNamingManager(instances);
		namingManager.setCoordinator(this);
		namingManager.setGrammarName(grammarName);
		namingManager.setInstanceName(instanceName);
		namingManager.setIsPrimaryGrammar(isPrimaryGrammar);

		isPrimaryGrammar = false;
		queue.add(namingManager);
	}

	public CharInputSequence getCharInputSequence(String grammarName)
			throws Exception {
		// messenger.println("Grammar Name: " + grammarName);
		File file = namespaceManager.getFileByGrammarName(grammarName);
		if (file == null) {
			throw new FileNotFoundException("cannot find the grammar file of '"
					+ grammarName + "'");
		}
		CharInputSequence charInputSequence = new FileCharInputSequence(file);
		return charInputSequence;
	}

	/**
	 * @param instanceName
	 * @param grammarName
	 */
	private void isAlreadyProcessed(String instanceName, String grammarName) {
		for (NamingManager namingManager : processed) {
			if (namingManager.getGrammarName().equals(grammarName)
					&& namingManager.getInstanceName().equals(instanceName)) {
				logger.info("already processed: " + grammarName + "/"
						+ instanceName);
				return;
			}
		}
	}

	/**
	 * @return the isPrimaryGrammar
	 */
	@Override
	public boolean isPrimaryGrammar() {
		return this.isPrimaryGrammar;
	}

	public JavaCCModel crosscompile(String grammarName) throws Exception {
		String namespaceName = getNamespaceByGrammarName(grammarName);

		namespaceManager.addNamespace(namespaceName);

		addGrammar("", grammarName, new HashMap<String, String>());

		NamingManager namingManager = queue.poll();

		defaultParserHandler.setNamingManager(namingManager);

		synthesisGrammar(namingManager, true);
		this.isPrimaryGrammar = false;

		while (!queue.isEmpty()) {
			namingManager = queue.poll();
			synthesisGrammar(namingManager, false);
		}

		javaccModel.setDefaultParserHandler(defaultParserHandler);
		return javaccModel;
	}

	public void setCache(Map<String, Object> cache) {
		this.cache = cache;
	}

	public void setDefaultParserHandler(
			DefaultParserHandler defaultParserHandler) {
		this.defaultParserHandler = defaultParserHandler;
	}

	/**
	 * @param grammarProduction
	 *            the grammarProduction to set
	 */
	public void setGrammarProduction(Production grammarProduction) {
		this.grammarProduction = grammarProduction;
	}

	public void synthesisGrammar(NamingManager namingManager,
			boolean firstGrammar) throws Exception {
		if (firstGrammar) {
			javaccModel.setGrammarName(namingManager.getGrammarName());
		}
		// messenger.println("new grammar: " + namingManager.getGrammarName());

		// build up the naming manager here.
		// NamingManager namingManager = getNamingManager(firstGrammar, g);
		cache.put("namingManager", namingManager);

		if (charInputSequence != null) {
			charInputSequence.close();
		}

		charInputSequence = getCharInputSequence(namingManager.getGrammarName());
		mappedLexer.setCharInputSequence(charInputSequence);

		this.grammarProduction.setLexer(mappedLexer);
		this.grammarProduction.parse();
	}

	/**
	 * @param mappedLexer
	 *            the mappedLexer to set
	 */
	public void setMappedLexer(MappedLexer mappedLexer) {
		this.mappedLexer = mappedLexer;
	}

	@Override
	public void finalize() throws Exception {
		queue.clear();
		processed.clear();
		this.mappedLexer.close();
	}
}
